package trunk;

import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridLayout;
import java.awt.Point;
import java.awt.Toolkit;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.font.TextAttribute;
import java.awt.geom.Rectangle2D;
import java.util.LinkedList;
import java.util.Queue;

import javax.print.attribute.Attribute;
import javax.print.attribute.AttributeSet;
import javax.print.attribute.DocAttributeSet;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JTextArea;
import javax.swing.JTextPane;

//This class will emulate console functionallity seen in other
//rougelikes using javas swing library

public class ConsoleGUI extends JFrame {
	// Constants for layout
	// Map location
	private static final int MAP_START_X = 0;
	private static final int MAP_START_Y = 0;
	private static final int MAP_END_X = 34;
	private static final int MAP_END_Y = 18;
	// Status Location
	private static final int STATUS_START_X = 35;
	private static final int STATUS_START_Y = 0;
	private static final int STATUS_END_X = 79;
	private static final int STATUS_END_Y = 18;
	// Message Location
	private static final int MESSAGE_START_X = 0;
	private static final int MESSAGE_START_Y = 19;
	private static final int MESSAGE_END_X = 79;
	private static final int MESSAGE_END_Y = 23;

	private JTextArea[][] text;
	private Color brush;
	Font f;
	private Level currentLevel;
	private boolean isLooking = false;
	private int lookx, looky = 0;
	private Queue<String> messageQueue;
	private Hero hero;

	// Tentatively this class will understand where to place each portion of the
	// screen while others will be unaware and will simply pass in whatever data
	// they generate

	// As such here is a definition of the screen layout as of now:
	/*
	 * It will be similar to Crawl's with a map, status information including,
	 * vital statistics (health, mana) as well as high priority character
	 * information such as what is wielded, how much gold, location, turns, etc.
	 * Finally there will be a section for messages that are user readable. In
	 * addition I currently feel that this class should eventually have a create
	 * and show menu method that will take in what to display and how and allow
	 * for other classes to send in parameter to make such menus.
	 */
	public ConsoleGUI() {
		text = new JTextArea[24][80];
		f = new Font("monospaced", Font.PLAIN, 12);
		brush = Color.GREEN;
		this.setLayout(new GridLayout(24, 80));
		for (int i = 0; i < 24; i++) {
			for (int j = 0; j < 80; j++) {
				text[i][j] = new JTextArea(1, 1);
				text[i][j].setBackground(Color.BLACK);
				text[i][j].setForeground(Color.GREEN);
				text[i][j].setFont(f);
				text[i][j].setText(" ");
				text[i][j].setEditable(false);
				text[i][j].addKeyListener(new Input());
				this.add(text[i][j]);
			}

		}
		this.addKeyListener(new Input());
		this.setSize(576, 396);
		this.setTitle("CraftSlash");
		this.setDefaultCloseOperation(EXIT_ON_CLOSE);
		this.setVisible(true);
		currentLevel = new Level();
		hero = currentLevel.getHero();
		messageQueue = new LinkedList<String>();
	}

	public void paint(Graphics g) {

		// this.setSize(g.getFontMetrics(f).getWidths()[67]*80,
		// g.getFontMetrics(f).getHeight()*24);
		// System.out.println("x,y"+this.getWidth()+","+this.getHeight());
		super.paint(g);

	}

	public void clearScreen() {
		for (int i = 0; i < 24; i++)
			for (int j = 0; j < 80; j++)
				text[i][j].setText(" ");
	}

	public void setTextColor(Color c) {
		brush = c;
	}

	// Prints a character to a specific location on the screen
	public void printChar(int x, int y, char c) {
		text[y][x].setText(String.valueOf(c));
		text[y][x].setForeground(brush);
		repaint();
	}

	// Prints a character of the specific color to the specific location
	// Color is restored after method call
	public void printCharColor(int x, int y, char c, Color color) {
		Color old = brush;
		brush = color;
		printChar(x, y, c);
		brush = old;
	}

	// Prints a string to a specifc location on the screen
	// If the string is too long it will be truncated
	public void printString(int x, int y, String str) {
		for (int i = x; i < (x + str.length()) && i < 80; i++) {
			printChar(i, y, str.charAt(i - x));
		}
	}

	// Prints a character of the specific color to the specific location
	// If the string is too long it will be truncated
	// Color is restored after method call
	public void printStringColor(int x, int y, String str, Color color) {
		Color old = brush;
		brush = color;
		printString(x, y, str);
		brush = old;
	}

	// Currently displays all empty cells
	public void updateMap() {
		// Must find location to center around aka where the hero is
		Point center = currentLevel.findHero();
		center.x += lookx;
		center.y += looky;
		// Move center if there aren't enough map tiles.
		if (center.x - (MAP_END_X - MAP_START_X) / 2 < 0) {
			center.x = (MAP_END_X - MAP_START_X) / 2;
		}
		if (center.y - (MAP_END_Y - MAP_START_Y) / 2 < 0) {
			center.y = (MAP_END_Y - MAP_START_Y) / 2;
		}
		if (center.x + (MAP_END_X - MAP_START_X) / 2 > currentLevel.getWidth()) {
			center.x = currentLevel.getWidth() - (MAP_END_X - MAP_START_X) / 2;
		}
		if (center.y + (MAP_END_Y - MAP_START_Y) / 2 > currentLevel.getHeight()) {
			center.y = currentLevel.getHeight() - (MAP_END_Y - MAP_START_Y) / 2;
		}
		// print characters from center - length of map/2 to center +length of
		// map/2
		// if level runs out of cells in a direction display map up to last cell
		for (int x = MAP_START_X; x < MAP_END_X; x++) {
			for (int y = MAP_START_Y; y < MAP_END_Y; y++) {
				Cell c = currentLevel.getCell(center.x
						- (MAP_END_X - MAP_START_X) / 2 + x, center.y
						- (MAP_END_Y - MAP_START_Y) / 2 + y);
				if (c.getLivingThing() instanceof LivingThing) {
					if (c.getLivingThing() instanceof Hero)
						printCharColor(x, y, '@', Color.WHITE);
					else if (c.getLivingThing() instanceof Monster)
						printCharColor(x, y, 'M', Color.RED);
				} else if (c.getType() == Cell.NORMAL_TILE)
					printChar(x, y, '.');
				else if (c.getType() == Cell.STONE_TILE)
					printCharColor(x, y, '#', Color.YELLOW);
				else if (c.getType() == Cell.CLOSED_DOOR_TILE)
					printCharColor(x, y, '+', Color.YELLOW);
				else if (c.getType() == Cell.OPEN_DOOR_TILE)
					printCharColor(x, y, '\'', Color.GREEN);
			}
		}
		this.repaint();
	}

	// Currently displays !nothing! empty fields at tentative locations
	public void updateStatus() {

	}

	// Eventually will take in messages to be printed to the user
	// add them to a queue and only flip through them when it is told
	// Currently displays sample strings
	public void printMessage(String str) {
		messageQueue.add(str);
	}

	public void updateMessage() {
		if (!messageQueue.isEmpty()) {
			printString(MESSAGE_START_X, MESSAGE_START_Y, messageQueue
					.element());
		} else {// Needs to clear the Message section but only if new messages
				// are added screen
			printString(MESSAGE_START_X, MESSAGE_START_Y,
					"                        ");
		}
	}

	public void clearMessage() {
		// 80
		// characters:12345678901234567890123456789012345678901234567890123456789012345678901234567890
		// String
		// clear="                                                                                ";
		// for(int i=0;i<(MAP_END_Y-MAP_START_Y);i++)
		// printString(MESSAGE_START_X,MAP_START_Y+i,"");
		while (!messageQueue.isEmpty())
			messageQueue.remove();
	}

	// Updates entire screen with current information
	public void update() {
		updateMap();
		updateMessage();
	}

	private class Input extends KeyAdapter {

		// Occurs when user presses a key
		public void keyPressed(KeyEvent k) {
			if (k.getKeyCode() == KeyEvent.VK_K
					|| k.getKeyCode() == KeyEvent.VK_UP) {
				if (isLooking) {
					looky--;
				} else {
					hero.moveN();
					currentLevel.sendTick();
				}
			} else if (k.getKeyCode() == KeyEvent.VK_J
					|| k.getKeyCode() == KeyEvent.VK_DOWN) {
				if (isLooking) {
					looky++;
				} else {
					hero.moveS();
					currentLevel.sendTick();
				}
			} else if (k.getKeyCode() == KeyEvent.VK_L
					|| k.getKeyCode() == KeyEvent.VK_RIGHT) {
				if (isLooking) {
					lookx++;
				} else {
					hero.moveE();
					currentLevel.sendTick();
				}
			} else if (k.getKeyCode() == KeyEvent.VK_H
					|| k.getKeyCode() == KeyEvent.VK_LEFT) {
				if (isLooking) {
					lookx--;
				} else {
					hero.moveW();
					currentLevel.sendTick();
				}
			} else if (k.getKeyCode() == KeyEvent.VK_U) {
				if (isLooking) {
					lookx++;
					looky--;
				} else {
					hero.moveNE();
					currentLevel.sendTick();
				}
			} else if (k.getKeyCode() == KeyEvent.VK_Y) {
				if (isLooking) {
					lookx--;
					looky--;
				} else {
					hero.moveNW();
					currentLevel.sendTick();
				}
			} else if (k.getKeyCode() == KeyEvent.VK_N) {
				if (isLooking) {
					lookx++;
					looky++;
				} else {
					hero.moveSE();
					currentLevel.sendTick();
				}
			} else if (k.getKeyCode() == KeyEvent.VK_B) {
				if (isLooking) {
					lookx--;
					looky++;
				} else {
					hero.moveSW();
					currentLevel.sendTick();
				}
			} else if (k.getKeyCode() == KeyEvent.VK_X) {
				// Needs to be fixed so that look does not increase out of
				// bounds of the map
				// even if the map is smart enough not to display it
				lookx = 0;
				looky = 0;
				isLooking = !isLooking;
				if (isLooking)
					printMessage("Looking");
				else
					clearMessage();
			} else if (k.getKeyCode() == KeyEvent.VK_O) {// Open doors
				for (Cell c : hero.getAdjacentCells()) {
					if (c.getType() == Cell.CLOSED_DOOR_TILE) {
						c.setType(Cell.OPEN_DOOR_TILE);
					}
				}
				currentLevel.sendTick();
			} else if (k.getKeyCode() == KeyEvent.VK_C) {// Close doors
				for (Cell c : hero.getAdjacentCells()) {
					if (c.getType() == Cell.OPEN_DOOR_TILE) {
						c.setType(Cell.CLOSED_DOOR_TILE);
					}
				}
				currentLevel.sendTick();
			} else if (k.getKeyCode() == KeyEvent.VK_S) {// Save
				System.out.println("Save:\n" + currentLevel.save());
			} else if (k.getKeyCode() == KeyEvent.VK_PERIOD) {
				currentLevel.sendTick();
			}
			update();
		}
	}
}
